package com.reactnativestudy.activity;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactInstanceManagerBuilder;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.LifecycleState;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.reactnativestudy.ApiInterface;
import com.reactnativestudy.BuildConfig;
import com.reactnativestudy.R;
import com.reactnativestudy.activity.MainActivity;
import com.reactnativestudy.model.ReactBundle;
import com.reactnativestudy.utils.Utils;

import net.lingala.zip4j.exception.ZipException;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executors;

import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
import pub.devrel.easypermissions.PermissionRequest;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;


public class ReactActivity extends AppCompatActivity {

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BuildConfig.BUNDLE_URL)
            .callbackExecutor(Executors.newSingleThreadExecutor())
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();

    ApiInterface request = retrofit.create(ApiInterface.class);

    private static final String TAG = "ReactActivity";
    ReactInstanceManager reactInstanceManager;
    private static final int STORAGE_PERMISSION_CODE = 0;

    ProgressBar progressBar;
    ReactRootView reactRootView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_react);

        requestStoragePermission();

    }

    private void preInitView() {
        reactRootView = findViewById(R.id.id_react_root_view);
        progressBar = findViewById(R.id.id_progress_bar);

        ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
                .setApplication(this.getApplication())
//                .setJSMainModulePath("index")
//                .setJSBundleFile(bundlePath + "app.android.bundle")
//                .setJSBundleFile(unZipPath + "app.android.js")
//                .setBundleAssetName("bundle-bussiness.js")s
//                .setJSBundleFile(unZipPath+"bundle-bussiness.js")
                .setJSBundleFile("assets://empty")

                .setCurrentActivity(this)
//                .setJSBundleLoader(jsBundleLoader)
//                .addPackages(Arrays.asList(new MainReactPackage()))
                .addPackages(new PackageList(this.getApplication()).getPackages())
                .setUseDeveloperSupport(false)
                .setInitialLifecycleState(LifecycleState.RESUMED);
        reactInstanceManager = builder.build();
        reactInstanceManager.createReactContextInBackground();
    }

    private void initView(ReactBundle reactBundle) {

        reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
            @Override
            public void onReactContextInitialized(ReactContext context) {
//
//                if (context != null) {
//
//                    CatalystInstance catalystInstance = context.getCatalystInstance();
//                    if (catalystInstance != null) {
//                        for (int i = 0; i < reactBundle.getDependencies().size(); i++) {
//                            ReactBundle bundle = reactBundle.getDependencies().get(i);
//                            catalystInstance.loadScriptFromFile(cachePath + "/" + bundle.getHash() + "/" + bundle.getEntry(), cachePath + "/" + bundle.getHash() + "/", false);
//                        }
//
//                        catalystInstance.loadScriptFromFile(cachePath + "/" + reactBundle.getHash() + "/" + reactBundle.getEntry(), cachePath + "/" + reactBundle.getHash() + "/", false);
//                    }
//                }
//
//                reactRootView.startReactApplication(reactInstanceManager, "ReactNativeStudy");
//                reactRootView.setVisibility(View.VISIBLE);
//                progressBar.setVisibility(View.GONE);
            }
        });

    }


    public static Intent makeIntent(Context context, String url){
        Intent intent = new Intent(context, MainActivity.class);
        intent.putExtra("url", url);
        return intent ;
    }

    private String getConfigUrl(){
        Intent intent = getIntent();
        if(intent != null){
            String url = intent.getStringExtra("url");
            if(url != null && !url.isEmpty()){
                return url;
            }
        }
        return BuildConfig.ENTRY_CONFIG_URL;
    }

    private void getBundle(Callback callback){
        String entry = Utils.getItem(getApplicationContext(), "entry");

        Log.v(TAG, entry);
//        if(entry != null && !entry.isEmpty()){
//            try {
//                callback.invoke( ReactBundle.toObject(entry));
//            } catch (JSONException e) {
//                e.printStackTrace();
//            }
//            return;
//        }

        request.getConfig(getConfigUrl())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ReactBundle>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                    }
                    @Override
                    public void onNext(ReactBundle reactBundle) {
                        callback.invoke(reactBundle);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });

    }

    private void init() {
       getBundle(new Callback() {
           @Override
           public void invoke(Object... args) {
               ReactBundle reactBundle = (ReactBundle) args[0];
               Utils.setItem(getApplicationContext(), "entry", reactBundle.toString());
               Log.v(TAG, reactBundle.toString());
               initBundle(reactBundle, a -> {
                   Log.v(TAG, "開始加載: "+System.currentTimeMillis()+"");
                   reactRootView.startReactApplication(reactInstanceManager, "ReactNativeStudy");
                   reactRootView.setVisibility(View.VISIBLE);
                   progressBar.setVisibility(View.GONE);

               });
           }
       });


    }


    private void loadBundle(ReactBundle reactBundle, String unZipPath) {
        ReactContext context = reactInstanceManager.getCurrentReactContext();
        if(context == null){
            return;
        }
        CatalystInstance catalystInstance = context.getCatalystInstance();
        if (catalystInstance != null) {
            catalystInstance.loadScriptFromFile(unZipPath + "/" + reactBundle.getEntry(), unZipPath + "/", false);
            Log.v(TAG, "加載: "+reactBundle.getName()+"  "+System.currentTimeMillis()+"");
        }

    }

    private void downloadAndLoadBundle(ReactBundle reactBundle, Callback callback) {
        String unZipPath = Utils.getCachePath(getApplication()) + "/" + reactBundle.getHash();
        if (Utils.isExists(unZipPath)) {
            loadBundle(reactBundle, unZipPath);
            callback.invoke();
            return;
        }
        Log.v(TAG, "開始下載：" + reactBundle.getBundle());
        request.download(reactBundle.getBundle()).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ResponseBody>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(ResponseBody responseBody) {
                        try {
                            String path = Utils.getCachePath(getApplication()) + "/" + reactBundle.getHash() + ".zip";
                            Utils.writeFileFromBytesByStream(path, responseBody.bytes(), false);
                            Utils.unZip(unZipPath, path);
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (ZipException e) {
                            e.printStackTrace();
                        }
                        loadBundle(reactBundle, unZipPath);
                        callback.invoke();
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {
                    }
                });
    }

    private void initBundle(ReactBundle reactBundle, Callback callback) {
        List<ReactBundle> dependencies = reactBundle.getDependencies();
        if (dependencies != null && dependencies.size() > 0) {

            this.initBundleDependencies(dependencies, new Callback() {
                @Override
                public void invoke(Object... args) {
                    downloadAndLoadBundle(reactBundle, callback);

                }
            });
        } else {
            downloadAndLoadBundle(reactBundle, callback);
        }
    }

    private void initBundleDependencies(List<ReactBundle> dependencies, Callback callback) {
        if (dependencies.size() == 0) {
            callback.invoke();
            return;
        }
        ReactBundle reactBundle = dependencies.get(0);
        this.initBundle(reactBundle, new Callback() {
            @Override
            public void invoke(Object... args) {
                dependencies.remove(0);
                initBundleDependencies(dependencies, callback);
            }
        });
    }

    @AfterPermissionGranted(STORAGE_PERMISSION_CODE)
    public void requestStoragePermission() {
        if (EasyPermissions.hasPermissions(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            // 获取空间存储权限
            Log.v(TAG, "requestStoragePermission: "+System.currentTimeMillis()+"");
            init();
            preInitView();
        } else {
            // 询问权限ƒ

            Log.d(TAG, "onPermissions : requestPermissions");

            EasyPermissions.requestPermissions(new PermissionRequest.Builder(this, STORAGE_PERMISSION_CODE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    .setRationale("R.string.permission_storage_message")
                    .setPositiveButtonText("確定")
                    .setNegativeButtonText("取消")
                    .build());
        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // 把请求权限的操作转交给EasyPermissions
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (reactInstanceManager != null) {
            reactInstanceManager.onHostResume(this);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (reactInstanceManager != null) {
            reactInstanceManager.onHostPause(this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (reactInstanceManager != null) {
            reactInstanceManager.onHostDestroy(this);
        }
    }

    @Override
    public void onBackPressed() {
        if (reactInstanceManager != null) {
            reactInstanceManager.onBackPressed();
        } else {
            super.onBackPressed();
        }
    }
}
